package com.qing.common.utils.sm;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import org.bouncycastle.util.encoders.Hex;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;

/**
 * @author guoqf
 */
public class Sm4Utils {

    private static String s = "294b319ec46955d7c47c22a1e74c6bd944c61bc945dbe89e5f8f6d9b991bc7144465700cfe909a0bf9c556eb32239274db96515265ff6c4db6ec039969f5348b";
    private static String k = "8db054787882cc7d5f5ea40fb34b47bc038afc8954fe7ccbc8903b6b16017be4";


    public static void main(String[] args) {

//        testEncrypt();
//        testSm4Encrypt();
//        testEncryptAndDecryptWithIv();
//        testEncryptAndDecrypt();
//        testBase64AndHexTime();


        String decryptStr = SmUtil.sm4(k.substring(24, 40).getBytes(StandardCharsets.UTF_8)).decryptStr(s);
        System.out.println("decryptStr : "+decryptStr);

    }

    private static void testSm4Encrypt() {
        // 创建SM4加密工具
        SM4 sm4 = new SM4();
        // 获取工具自己生成的密钥
        byte[] secKey = sm4.getSecretKey().getEncoded();
        System.out.println("Sm4 geneted key len : " + secKey.length);
        System.out.println(Hex.toHexString(secKey));

    }

    private static void testBase64AndHexTime() {
        int byteArraySize = 10000;
        byte[] cache = new byte[byteArraySize];
        Random random = new Random(47);
        for (int i = 0; i < cache.length; i++) {
            cache[i] = (byte) (random.nextInt(256) - 128);
        }

        int loopCount = 100;

        long start;


        start = System.currentTimeMillis();
        System.out.println("=========Hex编码开始");
        for (int i = 0; i < loopCount; i++) {
            HexUtil.encodeHexStr(cache);
        }
        System.out.println("=========Hex结束，用时：" + (System.currentTimeMillis() - start) + " 毫秒");

        start = System.currentTimeMillis();
        System.out.println("=========Base64编码开始");
        for (int i = 0; i < loopCount; i++) {
            Base64.encode(cache);
        }
        System.out.println("=========Base64结束，用时：" + (System.currentTimeMillis() - start) + " 毫秒");
    }

    private static void testEncrypt() {
//                String source1 = "0123456789abcdef";
        String source1 = "fedcba98765432";

        String source = "0123456789abcdeffedcba98765432";
        String key = "0123456789abcdef";
        String vi = "0123456789abcdef";


        String s;
//        s = sm4EncryptBase64(source, key);
//        System.out.println("sm4EncryptBase64Default: result=\n" + s + ",  hex size is: " + s.length());
        s = sm4EncryptHexDefault(source1, key);
        System.out.println("sm4EncryptHex source1: result=\n" + s + ",  hex size is: " + s.length());
        s = sm4EncryptHexDefault(source, key);
        System.out.println("sm4EncryptHex source: result=\n" + s + ",  hex size is: " + s.length());
//        System.out.println("sm4EncryptHex with vi: result=\n" + sm4EncryptHex(source, key,vi));
    }

    private static void testEncryptAndDecrypt() {
        //        String source = "0123456789abcdef";
//        String source = "fedcba9876543210";

        String source = "0123456789abcdeffedcba9876543210";
        String key = "0123456789abcdef";
        String vi = "0123456789abcdef";


        String encrypted;
//        encrypted = sm4EncryptBase64(source, key);
//        System.out.println("sm4EncryptBase64Default: result=\n" + encrypted + ",  hex size is: " + encrypted.length());
        System.out.println("sm4EncryptHex: source is: " + source);
        encrypted = sm4EncryptHex(source, key);
        System.out.println("sm4EncryptHex: result=\n" + encrypted + ",  hex size is: " + encrypted.length());
//        System.out.println("sm4EncryptHex with vi: result=\n" + sm4EncryptHex(source, key,vi));

        String decrypted = sm4DecryptHex(encrypted, key);

        System.out.println("sm4DecryptHex: decrypted is: " + decrypted);

    }

    private static void testEncryptAndDecryptWithIv() {
        //        String source = "0123456789abcdef";
//        String source = "fedcba9876543210";

        byte[] padding = new byte[16];
        Arrays.fill(padding, (byte) 16);


        String source = "0123456789abcdeffedcba9876543210";
        String key = "0123456789abcdef";
        String vi = "0123456789abcde";

        String encPadding = sm4EncryptHex(padding, key);
        System.out.println("加密后的padding: " + encPadding);
        String encrypted;
//        encrypted = sm4EncryptBase64(source, key);
//        System.out.println("sm4EncryptBase64Default: result=\n" + encrypted + ",  hex size is: " + encrypted.length());
        System.out.println("sm4EncryptHex with vi: source is: " + source);
        encrypted = sm4EncryptHex(source, key, vi);
        System.out.println("sm4EncryptHex: result=\n" + encrypted + ",  hex size is: " + encrypted.length());
//        System.out.println("sm4EncryptHex with vi: result=\n" + sm4EncryptHex(source, key,vi));

        String decrypted = sm4DecryptHex(encrypted, key, vi);

        System.out.println("sm4DecryptHex with vi: decrypted is: " + decrypted);

    }


    public static String sm4EncryptHexDefault(String source, String key) {
        System.out.println("=======Source's string length is :" + source.length());
        byte[] bytes = source.getBytes(StandardCharsets.UTF_8);
        System.out.println("=======Source's bytes size is :" + bytes.length + ", bytes is: " + ArrayUtil.join(bytes, ","));
        byte[] encrypted = new SM4(Mode.ECB, Padding.ZeroPadding, key.getBytes(StandardCharsets.UTF_8)).encrypt(bytes);
        System.out.println("=======Encrypted bytes size is :" + encrypted.length);

        return HexUtil.encodeHexStr(encrypted);
    }

    public static String sm4EncryptHexDefault(byte[] source) {
        System.out.println("=======Source's bytes size is :" + source.length + ", bytes is: " + ArrayUtil.join(source, ","));

        // 双方约定自定义的密钥
        String key = "0123456789abcdef";
        // 使用自定义密钥创建SM4加密工具
        SM4 sm4 = new SM4(key.getBytes(StandardCharsets.UTF_8));
        // 使用SM4加密工具加密数据
        byte[] cipher = sm4.encrypt(source);


        System.out.println("=======Encrypted bytes size is :" + cipher.length);

        return HexUtil.encodeHexStr(cipher);
    }

    public static String sm4DecryptHexDefault(byte[] cipher) {

        // 双方约定自定义的密钥
        String key = "0123456789abcdef";
        // 使用自定义密钥创建SM4解密工具
        SM4 sm4 = new SM4(key.getBytes(StandardCharsets.UTF_8));
        // 使用SM4解密工具解密数据
        byte[] source = sm4.decrypt(cipher);


        System.out.println("=======Encrypted bytes size is :" + cipher.length);

        return HexUtil.encodeHexStr(cipher);
    }

    public static String sm4EncryptHex(String source, String key, String iv) {
        byte[] srcBytes = source.getBytes(StandardCharsets.UTF_8);
        System.out.println("Source bytes");
        return new SM4(Mode.CBC, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8)).encryptHex(srcBytes);
    }

    public static String sm4DecryptHex(String source, String key, String vi) {
        return new SM4(Mode.CBC, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8), vi.getBytes(StandardCharsets.UTF_8)).decryptStr(source);
    }

    public static String sm4EncryptHex(String source, String key) {
        return new SM4(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8)).encryptHex(source);
    }

    public static String sm4EncryptHex(byte[] source, String key) {
        return new SM4(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8)).encryptHex(source);
    }

    public static String sm4DecryptHex(String source, String key) {
        return new SM4(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8)).decryptStr(source);
    }


    public static String sm4EncryptBase64(String source, String key) {
        return new SM4(Mode.ECB, Padding.PKCS5Padding, key.getBytes(StandardCharsets.UTF_8)).encryptBase64(source);
    }

}
